HyperCard 2.0 The Extended XCMD Interface
4 October 1990
External Windows How They Work
External windows are an extension of external commands (XCMDs and XFCNs). Two new callbacks, NewXWindow and GetNewXWindow, cause HyperCard to create a new window and save with it a reference to the XCMD that made the call. Then, whenever HyperCard receives an event from the Toolbox Event Manager which belongs to the window, it calls the XCMD that created the window with arguments that allow it to handle the event. Otherwise, HyperCard handles the event itself.
Whenever an XCMD is called by HyperCard, it receives a pointer to an XCMDBlock, just as it did in earlier versions of HyperCard.
  XCmdPtr   = ^XCmdBlock;
  XCmdBlock = RECORD
                paramCount     INTEGER;
                params         ARRAY[1..16] OF Handle;
                returnValue    Handle; 
                passFlag       BOOLEAN;
                entryPoint     ProcPtr; { to call back to HyperCard }
                request        INTEGER;
                result         INTEGER;
                inArgs         ARRAY[1..8] OF LongInt;
                outArgs        ARRAY[1..4] OF LongInt;
              END;

When HyperCard calls an XCMD to handle an event for an external window, some of the fields of the XCMDBlock have a new meaning. The paramCount field is set to -1, indicating that the XCMD has been called to handle an event. The first parameter, params[1],  is a pointer to an XWEventInfo block, defined as follows.
  XWEventInfoPtr = ^XWEventInfo;
  XWEventInfo    = RECORD
                     event       EventRecord;
                     eventWindow WindowPtr;
                     eventParams ARRAY[1..9] OF LongInt;
                     eventResult Handle;
                   END;

Therefore, an XCMD that manages an external window can be structured as follows
  IF paramPtr^.paramCount >= 0 THEN CreateMyWindow
  ELSE
    BEGIN
      WITH XWEventInfoPtr(paramPtr^.params[1])^ DO
        BEGIN
          myEvent = event;
          myWindow = eventWindow;
        END;
      SetPort(myWindow);
      CASE myEvent.what OF
        mouseDown DoMouse;
        etc...
      END;
    END;

When an external window is called to handle an event, it has full access to HyperTalk callback routines, just as it does when invoked from a HyperTalk script.

Events
HyperCard will automatically send most standard Macintosh events to XCMDs that manage external windows. These include
nullEvent                {see SetXWIdleTime}
mouseDown
updateEvt
activateEvt
app4Evt                {floaters should hide themselves when suspended and
                        show themselves when resumed}
Keyboard events are sent if the XCMD is the current editing environment
keyDown                {sent if BeginXWEdit has been called}
autoKey                {sent if BeginXWEdit has been called}

Keyboard events are delivered to the XCMD only if it has registered itself as the active editing environment, i.e. “has edit”, with the BeginXWEdit callback. All keystrokes, with the exception of command-key combinations recognized by the menu manager as equivalents of menu items, are sent to an XCMD when it has edit.
In addition, XCMDs will receive messages specific to HyperCard. These are delivered in the same EventRecord data structure used for standard Macintosh events, with the what field set to one of the following constants
  xOpenEvt              = 1000;         { the first event after you are created }
  xCloseEvt             = 1001;         { your window is being forced close (Quit?) }
  xGiveUpEditEvt        = 1002;         { you are losing Edit... }
  xGiveUpSoundEvt       = 1003;         { someone else is requesting HyperCard's sound channel }
  xHidePalettesEvt      = 1004;         { someone called HideHCPalettes }
  xShowPalettesEvt      = 1005;         { someone called ShowHCPalettes }
  xEditUndo             = 1100;         { Edit——Undo }
  xEditCut              = 1102;         { Edit——Cut }
  xEditCopy             = 1103;         { Edit——Copy }
  xEditPaste            = 1104;         { Edit——Paste }
  xEditClear            = 1105;         { Edit——Clear }
  xSendEvt              = 1200;         { script has sent you a message (text) }
  xSetPropEvt           = 1201;         { set a window property }
  xGetPropEvt           = 1202;         { get a window property }
  xCursorWithin         = 1300;         { cursor is within the window }
  xMenuEvt              = 1400;         { user has selected an item in your menu }
  xMBarClickedEvt       = 1401;         { a menu is about to be shown--update if needed }
  xShowWatchInfoEvt     = 1501;         { for variable and message watchers }
  xScriptErrorEvt       = 1502;         { place the insertion point }
  xDebugErrorEvt        = 1503;         { user clicked “Debug” at a complaint }
  xDebugStepEvt         = 1504;         { hilite the line }
  xDebugTraceEvt        = 1505;         { same as step but tracing }
  xDebugFinishedEvt     = 1506;         { script ended }
Handling Events
Many of the events above require special attention. Many are specific to the debugger, or other debugging tools. Here is a summary of all the events and their appropriate behavior
nullEvent
If an external window requires idle time and registers itself as such using the SetXWIdleTime callback, it is called periodically with a nullEvent.
mouseDown, updateEvt, app4Evt
These events are dispatched in the same manner as the Toolbox Event Manager. 
activateEvt
For external windows in the document layer (the same layer as the card window), activate/deactivate events are dispatched in the same manner as the Toolbox Event Manager. However, external windows in the floating layer never receive activate or deactivate events. Windows in the floating layer should always appear “active”. If your external window requires more modality, then place it in the document layer.
keyDown, autoKey
If an external window has become the active editing window by calling BeginXWEdit, it receives all keystrokes as standard Macintosh events.
xOpenEvt
After the XCMD which created the external windows completes execution, HyperTalk sends an xOpenEvt to the new external window. This is always the first event an external window receives. 
xCloseEvt
HyperTalk sends your external window an xCloseEvt when your window has called CloseXWindow, when another external window or XCMD has called CloseXWindow using your window’s WindowPtr, or when the user has used the new close window “windowName” command. The external window should not dispose of any of its data until receiving this event. If the external window sets passFlag to TRUE, the window is disposed. If not, the window is left open. Refer to the discussion of the CloseXWindow callback for more information.
xGiveUpEditEvt
When an external window calls BeginXWEdit, it will receive keystrokes and edit menu commands until the user clicks back in the card window, or activates some other editing window (eg the Message Box). HyperTalk signals the current editor with an xGiveUpEditEvt just before it activates the other editor.
xGiveUpSoundEvt
In the event that one external window requests the sound channel (with BeginXSound) while another external window has the channel, the current owner is given an xGiveUpSoundEvt. If the external window doesn’t set passFlag to true, the other external window’s callback returns with the result code set to xresFail. If an XCMD owns the sound channel, it can’t be notified of the second request, so that request will fail. The only exception to this is the HyperCard patch to _SysBeep; if an XCMD or external window owns the sound channel and someone calls SysBeep, HyperCard flashes the menubar rather than calling the XCMD.
xHidePalettesEvt
If another external window uses the HideHCPalettes callback, all external windows in the floating layer receive an xHidePalettesEvt. An example of this is the built-in Script Editor; it calls HideHCPalettes when the user edits a script.
xShowPalettesEvt
If another external window uses the ShowHCPalettes callback, all external windows in the floating layer receive an xShowPalettesEvt.
xEditUndo,xEditCut,xEditCopy,xEditPaste,xEditClear
While an external window has edit, it receives these events to correspond to the items in HyperCard’s built-in Edit menu.
xSendEvt
When a user issues the send “message” to window “windowName” command, or when an XCMD or external window issues the SendWindowMessage callback, the external command receives an xSendEvt. When this event is received, eventParams[1] contains a pointer to a Pascal string (Str255) containing the name of the message. This is done for speed purposes and to expedite using the StringEqual callback to index through the commands your window supports.
xSetPropEvt,xGetPropEvt
HyperTalk contains new extensible syntax for getting and setting properties of external windows. The syntax is
	get “property” of window “windowName”
	set “property” of window “windowName” to “propertyValue”

HyperTalk has two built-in properties of external windows loc and visible. If an external window doesn’t do anything special in response to being moved or shown/hidden, it can set passFlag to TRUE in response to both xSetPropEvt and xGetPropEvt and HyperTalk will handle the request. If a property is requested other than the two built-in ones and the external window passes the event, HyperTalk will complain to the user.
If the external window wishes to respond specially to these requests, or if it has additional properties it wants to support, it can directly handle all of HyperTalk’s requests. In both xSetPropEvt and xGetPropEvt, eventParams[1] contains a pointer to a Pascal string (Str255) containing the name of the property. In the case of a xSetPropEvt, eventParams[2] contains a handle to the propertyValue. In the case of an xGetPropEvt, the external window must return a handle in the eventResult field with the value of the property requested.
HyperTalk uses property gets and sets to translate the hide and show commands. In the event the user says hide window “Variable Watcher”, HyperTalk informs the external window by giving it an xSetPropEvt with the property “visible” and the value “false”.
For an example of the ability of external windows to use properties, try these properties of the Message Watcher window loc, visible, hideIdle, hideUnused, text, and lastLine (set only). The Variable Watcher supports the following properties loc, visible, hBarLoc, vBarLoc, and rect. 
For the convenience of all concerned, there are four new callbacks to aid in getting and setting properties PointToStr, RectToStr, StrToPoint, and StrToRect.
xCursorWithin
This event is sent when the cursor is over any part of the external window. If the external window sets passFlag to TRUE, HyperCard sets the cursor to an arrow.
xMenuEvt
When an external window has used RegisterXWMenu for one or more menus in HyperCard’s menubar, HyperTalk notifies it of an item being chosen by sending it an xMenuEvt. This holds true for command-key equivalents as well. When the event is received, eventParams[1] is the menu ID and eventParams[2] is the menu item.
xMBarClickedEvt
If an external window has registered one or more menus in the menubar, HyperTalk sends it an xMBarClickedEvt just before it calls the Menu Manager’s MenuSelect or MenuKey routines. This is to allow the external window to adjust its menus just before the user sees them.
xShowWatchInfoEvt
Sent to the Message Watcher and Variable Watcher whenever it is appropriate. The message watcher gets eventParams[1] set to a handle to the current message, indented with 2 space characters for each level of nesting. The variable watcher gets no arguments.
xScriptErrorEvt
Sent to the current script editor when HyperTalk displays the “Debug, Script, or Cancel” alert and the user clicks “Script”. eventParams[1] contains the line number on which the error occurred.
xDebugErrorEvt,xDebugStepEvt,xDebugTraceEvt
Sent to the current debugger window.
xDebugFinishedEvt
Sent to all external windows when the user chooses “Go” or “Abort” in the debugger.
Closing an External Window
External windows should only dispose of their private data structures in response to an xCloseEvt. Whenever an XCMD calls CloseXWindow, it should not dispose of any of its internal information until it receives its xCloseEvt.
When an xCloseEvt is sent, the XCMD must set passFlag to true if it actually wants to close. If it does not set passFlag, it is telling HyperTalk that it wants to cancel the close operation. This is useful for editors which put up a “Save this document?” dialog which includes a “Cancel” button.
Special XCmdBlock Values
Because special XCMDs like the Message Watcher and Script Editor/Debugger are XCMDs, there had to be some defense against a user typing ScriptEditor in the message box and launching the script editor. Therefore, the special-case XCMDs are given special paramBlocks when they are opened by HyperTalk.
Message Watcher
When HyperTalk calls the Message Watcher to initialize itself, it sets paramPtr^.paramCount to xMessageWatcherID (-2). There are no params sent. The Message Watcher is called at startup time and is always present. If you switch to another Message Watcher (using set the messageWatcher to “MyWatcher”), that watcher is loaded immediately. The normal behavior of the Message Watcher is that it should be inactive when it is invisible.
Variable Watcher
When HyperTalk calls the Variable Watcher to initialize itself, it sets paramPtr^.paramCount to xVariableWatcherID (-3). There are no params sent. The Variable Watcher is called at startup time and is always present. If you switch to another Variable Watcher (using set the variableWatcher to “MyWatcher”), that watcher is loaded immediately. The normal behavior of the Variable Watcher is that it should be inactive when it is invisible.
Script Editor
When HyperTalk calls a Script Editor to initialize itself, it sets paramPtr^.paramCount to xScriptEditorID (-4). There are three params sent. params[1] is a zero-terminated handle to the script of the object. (unindented). params[2] is a pointer to a Pascal string (Str255) containing the name of the window as proposed by HyperTalk (eg script of card button id 4 = “Fred”). params[3] is a pointer to the XTalkObject describing the object to be edited (see XTalkObject below). 
Note There can be multiple script editors open at once, all sharing the same copy of the XCMD resource in memory. For this reason, XCMDs must not write to their own own code, or serious consequences can arise!
Debugger 
A debugger window is initialized the same as a Script Editor, with the exception that the paramPtr^.paramCount is set to xDebuggerID (-5).

XTalkObject
In order to increase a script editor’s flexibility, it can communicate with HyperTalk using a special data structure called an XTalkObject. Each time a script editor is opened, it is passed a pointer to this structure and describing the object whose script is being edited. However, any XCMD can use it as long as the relevant fields are filled in correctly.
  XTalkObjectPtr = ^XTalkObject;
  XTalkObject = RECORD
                  objectKind   INTEGER;        { stack, bkgnd, card, field, or button }
                  stackNum     LongInt;        { reference number of the source stack }
                  bkgndID      LongInt;
                  cardID       LongInt;
                  buttonID     LongInt;
                  fieldID      LongInt;
                END;

objectKind
The type of object stackObj, bkgndObj, cardObj, fieldObj, and buttonObj.
stackNum
The reference number of the stack containing the object. Use StackNameToNum to get the stacknum of a stack when you know the name. If XTalkObject.objectKind =  stackObj, this is the last relevant field. Use GetObjectName to get the name of the stack if all you have is the stacknum. 
bkgndID
The ID of the background. If XTalkObject.objectKind =  bkgndObj, this is the last relevant field. If the object is a card, this is the background to which the card belongs. If the object is a background field or button, this is the ID of the bkgnd to which the field or button belongs.
cardID
The ID of the card. If XTalkObject.objectKind =  cardObj, this is the last relevant field. If the object is a card field or button, this is the ID of the card to which the field or button belongs.
buttonID
If the button is a card button, this value is negative, ABS(buttonID) is the ID of the button, and cardID is the owner of the button. If the button is a background button, this value is positive, buttonID is the ID of the button, and bkgndID is the owner of the button. 
fieldID
If the field is a card field, this value is negative, ABS(fieldID) is the ID of the field, and cardID is the owner of the field. If the field is a background field, this value is positive, fieldID is the ID of the field, and bkgndID is the owner of the field. 

Window layer management
Within HyperCard, each window resides in one of two layers, the floating layer or the document layer. 
The floating layer is reserved for windows that have a single state, such as HyperCard’s tool window, which is always active when it is visible. All of HyperCard’s floating windows, including the message box and the tool window, reside in this layer. Every visible window in the floating layer is active at all times. Windows in this layer never receive activate and deactivate events.
The rear layer is the document layer. Windows that have multiple states, active and inactive, such as HyperCard’s card windows and script editing windows, reside in this layer. Only one window in this layer is active at a time.

XCMDs determine the layer in which their external windows reside using the floating argument to GetNewXWindow and NewXWindow. If floating is TRUE, the window is placed at the front of the floating layer, otherwise it is placed at the front of the document layer.
FUNCTION  NewXWindow(paramPtr XCmdPtr; boundsRect Rect; title Str255; visible BOOLEAN;
                     procID INTEGER; color BOOLEAN; floating BOOLEAN) WindowPtr;		
FUNCTION  GetNewXWindow(paramPtr XCmdPtr; templateType ResType; templateID INTEGER;
                     color BOOLEAN; floating BOOLEAN) WindowPtr;

An XCMD can check the frontmost window of the floating layer using the Window Manager’s FrontWindow function. An XCMD can use the FrontDocWindow XCMD callback find the frontmost window in the document layer.

Memory Management Tips
HyperCard 2.0 contains tremendous amounts of new functionality, and one of its new features was implemented in a way that may conflict with a generally-accepted XCMD and Macintosh Toolbox programming convention.
The Macintosh Memory Manager will allocate two different types of heap blocks relocatable and non-relocatable. Nonrelocatable blocks (“pointers”) are allocated as low as possible in the heap, and relocatable blocks (“handles”) are allocated wherever possible. A problem will arise if an XCMD or external window leaves a non-relocatable block (or a locked relocatable block) allocated after it returns control to HyperCard, and the user attempts to go to a stack that is larger than classic size (512 pixels wide and 342 pixels high). This condition is commonly called an island in the heap. Even though HyperTalk’s heapSpace function may indicate lots of free memory, the user will be limited to classic card size, scrolling will be slower, and they will be prevented from using the Painting tools.
Follow the simple rules below and life will be far richer for your XCMD and external window users
1)	Nonrelocatable blocks are OK to use temporarily, but do not leave them allocated when the XCMD returns control to HyperCard. 
2)	Locked relocatable blocks are also OK to use, but try to lock the block only when it is actually being accessed, then either dispose it or unlock it.
3)	If an XCMD requires a block to be locked permanently, it should call NewHandle, and if that call is successful, it should call MoveHHi before it calls HLock. Some more complex XCMDs may contain code meant to run at interrupt level or code (such as ROM patches) to which absolute addresses must be calculated. Once a relocatable block is locked, that block is just as immovable as a nonrelocatable block, but much nicer from the HyperCard user’s perspective.
4)	Also, be careful with callbacks such as SendCardMessage while blocks are locked in the heap, because these callbacks can cause HyperCard to change stacks if the message issues a go command.

Callbacks
The XCMD programming interface is implemented through the use of callbacks. The complete list of callbacks follows. New HyperCard 2.0 callbacks are denoted by ◊.
HyperTalk Utilities  
FUNCTION EvalExpr(paramPtr XCmdPtr; expr Str255) Handle;
EvalExpr evaluates the HyperTalk expression passed in expr and returns a handle to a zero-terminated string containing the result of the evaluation. For example, EvalExpr('the long date') returns a handle to a string containing the current date in the long format (Saturday, June 25, 1988). The caller must dispose of the handle. If the callback fails to evaluate properly because of an error, EvalExpr returns NIL and paramPtr^.result is set to xresFail.
PROCEDURE SendCardMessage(paramPtr XCmdPtr; msg Str255);
The string in msg is sent as a message to the current card.
PROCEDURE SendHCMessage(paramPtr XCmdPtr; msg Str255);
The string in msg is sent as a message directly to HyperCard, bypassing all HyperTalk handlers.
◊ PROCEDURE RunHandler(paramPtr XCmdPtr; handler Handle);
RunHandler is similar to the HyperTalk do command. The zero-terminated string in handler is interpreted first as a message. If it is a message (command or function), it is sent to the current card. If it is one line of HyperTalk, it is executed as though it were in the message box. The text can contain multiple lines of HyperTalk, including conditional statements and repeat loops. If it is multiple lines of HyperTalk, the lines are also executed as though from the message box. If it is multiple lines beginning with on <messageName> and ending with end <messageName>, messageName is “sent” to this handler in the context of the card. If the handler exits, execution terminates. If the handler contains the line pass <messageName>, the messageName is passed up HyperTalk’s normal inheritance chain, beginning with the current card script.
Note You can not override a script executed with RunHandler. In other words, if the current card has an on doSomething handler, and an XCMD issues a RunHandler callback with an on doSomething, the XCMD’s handler will be executed. If the handler passes the message, the card’s script will be executed.
Another note Execution is somewhat slower using RunHandler than it would be running the same script as a card handler. You also can not use the debugging tools to debug these scripts.
A Word About HyperTalk Errors In normal HyperTalk script execution, an alert is displayed when a script encounters an error. For example, entering the long tade (“date” is misspelled) in the message box will display a “Can’t understand…” message. This alert is called a complaint. When HyperTalk displays a complaint, it terminates all pending handlers and returns to idle. This is the same behavior that takes place when a user types Command-Period while running a script. Since HyperCard 1.2, HyperTalk doesn’t display a complaint when a script error is encountered by an XCMD callbacks. This holds true not only for messages themselves, but any handlers called by those messages.
If a callback encounters a script error, execution of the callback stops and control is returned to the XCMD. Whenever an XCMD makes a HyperTalk callback, it should check paramPtr^.result. If result is not xresSucc, then the callback failed. Depending on the situation, the XCMD might elect to display an appropriate error message and terminate execution. For example, the assumption that EvalExpr always returns a valid handle is dangerous, because in the event that it fails, EvalExpr returns NIL. Trying to manipulate a NIL handle can result in a System Error (bomb).

Memory Utilities  
FUNCTION GetGlobal(paramPtr XCmdPtr; globName Str255) Handle;
GetGlobal returns a handle to a zero-terminated string that contains a copy of the contents of the HyperTalk global variable globName. If globName doesn’t exist, GetGlobal returns a handle to an empty string. The caller must dispose of the handle.
PROCEDURE SetGlobal(paramPtr XCmdPtr; globName Str255; globValue Handle);
SetGlobal copies the zero-terminated string to which globValue is a handle into the HyperTalk global variable named globName. If globName doesn’t exist, SetGlobal creates it. HyperCard does not dispose of globValue.
PROCEDURE ZeroBytes(paramPtr XCmdPtr; dstPtr Ptr; longCount LongInt);
ZeroBytes sets longCount bytes beginning at dstPtr to 0. It performs no boundary checking. For example, it can write past the end of a zero-terminated string.

String Utilities
PROCEDURE ScanToReturn(paramPtr XCmdPtr; VAR scanPtr Ptr);
ScanToReturn scans along the zero-terminated string pointed to by scanPtr, stopping just past the first return character or at the end of the string. scanPtr is incremented to point to the new location.
PROCEDURE ScanToZero(paramPtr XCmdPtr; VAR scanPtr Ptr);
ScanToZero scans along the zero-terminated string pointed to by scanPtr, stopping at the end of the string. scanPtr is incremented to point to the new location.
FUNCTION StringEqual(paramPtr XCmdPtr; str1,str2 Str255) BOOLEAN;
StringEqual compares the two Pascal strings str1 and str2 (case-insensitive and diacritical-sensitive) and returns TRUE if the two strings are identical; otherwise it returns FALSE.
FUNCTION StringLength(paramPtr XCmdPtr; strPtr Ptr) LongInt;
StringLength returns the number of characters in the zero-terminated string pointed to by strPtr. Note that strPtr is a pointer, not a handle.
FUNCTION StringMatch(paramPtr XCmdPtr; pattern Str255; target Ptr) Ptr;
StringMatch performs a case-insensitive search for pattern (a Pascal string) in the zero-terminated string pointed to by target. If the search is successful, the location of the first matching character is returned as the function result. If the search is unsuccessful, StringMatch returns nil. This is equivalent to HyperTalk’s offset function.
◊ PROCEDURE ZeroTermHandle(paramPtr XCmdPtr; hndl Handle);
ZeroTermHandle grows the block referenced by hndl by one byte and then sets the extra byte to 0, making hndl legal for operations such as SetGlobal and ZeroToPas.

String Conversions
PROCEDURE BoolToStr(paramPtr XCmdPtr; bool BOOLEAN; VAR str Str255);
BoolToStr converts bool to a Pascal string (“true” or “false”).
PROCEDURE ExtToStr(paramPtr XCmdPtr; num Extended; VAR str Str255);
ExtToStr converts num (a SANE extended type) to a Pascal string.
PROCEDURE LongToStr(paramPtr XCmdPtr; posNum LongInt; VAR str Str255);
LongToStr converts posNum (a 32-bit unsigned integer) to a Pascal string.
PROCEDURE NumToHex(paramPtr XCmdPtr; num LongInt; nDigits INTEGER; VAR str Str255);
NumToHex returns in str a hexidecimal (base 16) representation of the value of num, expanding the string to nDigits in length.
PROCEDURE NumToStr(paramPtr XCmdPtr; num LongInt; VAR str Str255);
NumToStr converts num (a 32-bit signed integer) to a Pascal string.
FUNCTION PasToZero(paramPtr XCmdPtr; str Str255) Handle;
PasToZero converts str to a zero-terminated string and returns a handle to the new string. The caller must dispose of the handle.
PROCEDURE ReturnToPas(paramPtr XCmdPtr; zeroStr Ptr; VAR pasStr Str255);
ReturnToPas copies characters from the zero-terminated string pointed to by zeroStr into the Pascal string pasStr, stopping at the first return character (ASCII $0D), the end of the zero-terminated string, or the 255th character, whichever comes first.
FUNCTION StrToBool(paramPtr XCmdPtr; str Str31) BOOLEAN;
StrToBool converts str to a boolean (TRUE or FALSE) and returns the boolean value as its result.
FUNCTION StrToExt(paramPtr XCmdPtr; str Str31) Extended;
StrToExt converts str to an extended type. Extended numbers contain a sign bit, 15 bits for the exponent and 63 bits for the significand. This is the standard data type for SANE, the Standard Apple Numeric Environment.
FUNCTION StrToLong(paramPtr XCmdPtr; str Str31) LongInt;
StrToLong converts str to a long (32-bit) unsigned integer. Unsigned long integers range from 0 to 4,294,967,296.
FUNCTION StrToNum(paramPtr XCmdPtr; str Str31) LongInt;
StrToNum converts str to a long (32-bit) signed integer. Signed long integers range from (-2,147,483,648) to 2,147,483,648.
PROCEDURE ZeroToPas(paramPtr XCmdPtr; zeroStr Ptr; VAR pasStr Str255);
ZeroToPas converts the zero-terminated string pointed to by zeroStr to a Pascal string and returns the string in pasStr.
◊ PROCEDURE PointToStr(paramPtr XCmdPtr; pt Point; VAR pasStr Str255);
PointToStr converts the point passed in pt to a Pascal string and returns the string in pasStr. 
◊ PROCEDURE RectToStr(paramPtr XCmdPtr; rct Rect; VAR pasStr Str255);
RectToStr converts the rectangle passed in rct to a Pascal string and returns the string in pasStr. 
◊ PROCEDURE StrToPoint(paramPtr XCmdPtr; pasStr Str255; VAR pt Point);
StrToPoint converts the Pascal string passed in pasStr to a point and returns the point in pt. 
◊ PROCEDURE StrToRect(paramPtr XCmdPtr; pasStr Str255; VAR rct Rect);
StrToRect converts the Pascal string passed in pasStr to a rectangle and returns the rectangle in rct. 

Field Utilities
FUNCTION GetFieldByID(paramPtr XCmdPtr; cardFld BOOLEAN; fldID INTEGER) Handle;
GetFieldByID returns a handle to a zero-terminated string that contains a copy of the contents of field ID fldID. If cardFld is TRUE, fldID is a card field; otherwise it is a background field.
FUNCTION GetFieldByName(paramPtr XCmdPtr; cardFld BOOLEAN; fldName Str255) Handle;
GetFieldByName returns a handle to a zero-terminated string that contains a copy of the contents of field fldName. If cardFld is TRUE, fldName is a card field; otherwise it is a background field.
FUNCTION GetFieldByNum(paramPtr XCmdPtr; cardFld BOOLEAN; fldNum INTEGER) Handle;
GetFieldByNum returns a handle to a zero-terminated string that contains a copy of the contents of field number fldNum. If cardFld is TRUE, fldNum is a card field; otherwise it is a background field.
PROCEDURE SetFieldByID(paramPtr XCmdPtr; cardFld BOOLEAN; fldID INTEGER;
                       fldVal Handle);
SetFieldByID copies the zero-terminated string to which fldVal is a handle into the field ID fldID. If cardFld is TRUE, fldID is a card field; otherwise it is a background field. The caller must dispose of the handle.
PROCEDURE SetFieldByName(paramPtr XCmdPtr; cardFld BOOLEAN; fldName Str255;
                         fldVal Handle);
SetFieldByName copies the zero-terminated string to which fldVal is a handle into field fldName. If cardFld is TRUE, fldName is a card field; otherwise it is a background field. The caller must dispose of the handle.
PROCEDURE SetFieldByNum(paramPtr XCmdPtr; cardFld BOOLEAN; fldNum INTEGER;
                        fldVal Handle);
SetFieldByNum copies the zero-terminated string to which fldVal is a handle into the field number fldNum. If cardFld is TRUE, fldNum is a card field; otherwise it is a background field. The caller must dispose of the handle.
◊ FUNCTION  GetFieldTE(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldID, fieldNum
                       INTEGER; fieldNamePtr StringPtr) TEHandle;
GetFieldTE returns a copy of the Styled TEHandle from the specified field, including style runs (see Inside Macintosh Volume V). The caller must dispose of this TEHandle.
To determine which field the XCMD wants to manipulate, HyperTalk uses the following logic if fieldID is non-zero, then HyperTalk uses it. If fieldNum is non-zero, then HyperTalk uses it. Finally, if fieldNamePtr is not NIL, HyperTalk uses the field name pointed to by it. If GetFieldTE returns NIL, the field was not found or there wasn’t enough memory to copy the text and styles. The caller must dispose of this TEHandle.
Note the XCMD should not use the TEHandle returned by this callback before adjusting the TEHandle^^.inPort field to point to another grafPort. HyperTalk sets this field to point to the card window when this callback returns, so routines like TEUpdate and TESetSelect may cause the text to draw into the card window.
◊ PROCEDURE SetFieldTE(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldID, fieldNum
                       INTEGER; fieldNamePtr StringPtr; fieldTE TEHandle);
SetFieldTE sets the text and styles of the field to the text and styles contained in fieldTE. See GetFieldTE for a description of the use of fieldID, fieldNum, and fieldNamePtr. The caller must dispose of this TEHandle.
Miscellaneous Utilities
◊ PROCEDURE BeginXSound(paramPtr XCmdPtr; window WindowPtr);
An XCMD should call BeginXSound before it attempts to allocate a sound channel or perform any other Sound Manager operation. After an XCMD calls BeginXSound, HyperCard’s built-in play command will not operate until the XCMD calls EndXSound (see below). If an external window is making the callback, it should pass a pointer to its window in the window parameter. An XCMD should pass NIL for the window parameter. An XCMD or an external window can optionally pass a pointer to a valid external window in the window parameter if it wants to “aim” the call at another external window. If HyperTalk gets a valid windowPtr in window, it will post an xGiveUpSoundEvt to that window at an appropriate time. If the XCMD passes NIL for window, HyperCard will be unable to signal the XCMD when HyperCard needs the sound channel back.
An XCMD that uses the Sound Manager can be structured as follows
BeginXSound(paramPtr,NIL);
(* allocate a sound channel *)
(* make some noise *)
(* deallocate sound channel *)
EndXSound(paramPtr);
◊ PROCEDURE EndXSound(paramPtr XCmdPtr);
EndXSound informs HyperCard that an XCMD has finished using the Sound Manager. If an XCMD has not previously called BeginXSound, EndXSound does nothing.
◊ FUNCTION GetFilePath(paramPtr XCmdPtr; fileName Str255; numTypes INTEGER;
                       typeList SFTypeList; askUser BOOLEAN;
                       VAR fileType OSType; VAR fullName Str255) BOOLEAN;
GetFilePath determines the full pathname of the file fileName using the search paths stored in the Home stack. If fileType is 'STAK', GetFilePath uses the stack search paths. If fileType is 'APPL', GetFilePath uses the application search paths. If fileType is neither 'STAK' nor 'APPL', GetFilePath uses the document search paths. numTypes and typeList are used as they are described in Inside Macintosh Volume I, page 523. If askUser is TRUE and HyperCard fails to find the file on its own, GetFilePath prompts the user to find the file with a Standard File dialog. If the file is located either by HyperCard or by the user, the full pathname of the file is returned in fullName and the file type is returned in fileType. If the user clicks “Cancel” in the Standard File dialog or HyperCard fails to find the file for any other reason, GetFilePath returns FALSE.
◊ PROCEDURE GetXResInfo(paramPtr XCmdPtr; VAR resFile INTEGER;
                         VAR resID INTEGER; VAR rType ResType;
                         VAR name Str255);
GetXResInfo returns the file reference number of the resource file from which the calling XCMD was read in resFile and the resource ID of the XCMD in resID, the resource type (XCMD or XFCN) in rType, and the resource name in name.
◊ PROCEDURE Notify(paramPtr XCmdPtr);
If HyperCard is active or MultiFinder is not loaded, Notify returns immediately. Otherwise, Notify blinks the small HyperCard icon over the  menu until the user switches to HyperCard’s layer. Only then does Notify return. No other HyperCard processing takes place while Notify is waiting.
◊ PROCEDURE SendHCEvent(paramPtr XCmdPtr; event EventRecord);
SendHCEvent is useful only to XCMDs that call the ToolBox routines GetNextEvent or WaitNextEvent. Such XCMDs should use SendHCEvent to “pass” events required by HyperCard. For example, an XCMD that creates a draggable window and calls GetNextEvent may receive update events for HyperCard’s windows.  HyperCard will perform the updates if it receives the update events via this callback. More importantly, an XCMD that calls GetNextEvent and receives an app4Evt generated by MultiFinder must pass the event along to HyperCard. If HyperCard does not receive its suspend and resume events, bad things can happen.
XCMDs that create windows by means of the NewXWindow callback don’t need to call GetNextEvent or WaitNextEvent and therefore don’t need to use SendHCEvent. 
◊ PROCEDURE SendWindowMessage(paramPtr XCmdPtr; windPtr WindowPtr;
                                                             windowName Str255; msg Str255);
SendWindowMessage is functionally equivalent to send <msg> to window <windowName> from HyperTalk. Use this for direct communication between XCMDs that manage external windows. If windPtr is not NIL, the window pointer is used to determine which window will receive the message, otherwise the name is used.
◊ FUNCTION  FrontDocWindow(paramPtr XCmdPtr) WindowPtr;
FrontDocWindow returns the WindowPtr of the document which is frontmost in HyperCard’s document layer. This will not return the WindowPtr of a window in the palette layer. To return the frontmost window of any type, use the Window Manager’s FrontWindow function.
◊ FUNCTION  StackNameToNum(paramPtr XCmdPtr; stackName Str255) LongInt;
Internally, HyperCard no longer only remembers stacks by their name. It uses a stack number to represent the stack. This number is akin to a volume reference number it is valid as an indicator as long as the application is open, but won’t be valid across multiple launches. This number is valid when used in an XTalkObject to get and set the scripts of objects. StackNameToNum translates the name of a stack into this number.
◊ FUNCTION  ShowHCAlert(paramPtr XCmdPtr; dlgID INTEGER;
                        promptStr Str255) INTEGER;
HyperCard contains four standard alert windows. XCMDs can use these, along with all of the smart window-centering, text-expanding, and command-key handling without having to use the answer and ask commands any more. dlgID must be one of the following constants. 
  errorDlgID            = 1;    { 1OK (default) }
  confirmDlgID          = 2;    { 1OK (default) and 2Cancel }
  confirmDelDlgID       = 3;    { 1Cancel (default) and 2Delete }
  yesNoCancelDlgID      = 4;    { 1Yes (default), 2Cancel, and 3No }
The values listed in the comments correspond to the function results returned by ShowHCAlert. For example
CASE ShowHCAlert(paramPtr,yesNoCancelDlgID,'Delete file "Fred?"') OF
  1 DeleteTheFile;       { user chose “OK” }
  2 Abort;               { user chose “Cancel” }
  3 Continue;            { user chose “No” }
END;
The pascal string in promptStr can contain the Dialog Manager’s wildcard characters (^0, ^1, ^2, and ^3) if the XCMD calls ParamText before calling ShowHCAlert.

Creating and Disposing External Windows
◊ FUNCTION  GetNewXWindow(paramPtr XCmdPtr; templateType ResType;
                          templateID INTEGER; color BOOLEAN;
                          floating BOOLEAN) WindowPtr;
GetNewXWindow creates a new window or dialog from a resource. If the Window Manager fails to create the window, GetNewXWindow returns NIL. If the window is created successfully, GetNewXWindow sets up the mechanism by which events pertaining to the window are sent to the XCMD that created it.
templateType must be either 'WIND' or 'DLOG'. templateID is the resource ID of the window or dialog template to be used. The template resource can exist in any resource file that’s currently open.
If colorWind is TRUE, HyperTalk attempts to create a color window using the GetNewCWindow or NewCDialog toolbox trap. If colorWind is TRUE and Color QuickDraw is not present, the window is not created and GetNewXWindow returns NIL.
Note GetNewXWindow is compatible with non-standard window definition functions (see NewXWindow below for more information).
◊ FUNCTION  NewXWindow(paramPtr XCmdPtr; boundsRect Rect; title Str255;
                       visible BOOLEAN; procID INTEGER; color BOOLEAN;
                       floating BOOLEAN) WindowPtr;
NewXWindow creates a new window and returns a pointer to it as the function’s result. If the Window Manager fails to create the window, NewXWindow returns NIL. If the window is created successfully, NewXWindow sets up the mechanism by which events pertaining to the window are sent to the XCMD that created it.
If colorWind is TRUE, HyperTalk attempts to create a color window using the NewCWindow toolbox trap. If colorWind is TRUE and Color QuickDraw is not present, the window is not created and NewXWindow returns NIL.
procID is the same as the procID argument to the Window Manager routine NewWindow. For example, passing documentProc as the procID produces a standard document window with no zoom box. boundsRect is the bounding rectangle for the window in global coordinates. title becomes the title of the window.
The visible argument determines whether the window will be created visible.
To create windows similar to HyperCard’s palettes, such as the tool window and the message box, use HyperCard’s built-in window definition function for palettes. Here are the values to use for procID
  paletteProc		= 2048;	{ Palette with grow box }
  palNoGrowProc		= 2052;	{ standard Palette defproc } 
  palZoomProc		= 2056;	{ Palette with zoom and grow }
  palZoomNoGrow		= 2060;	{ Palette with zoom and no grow }
  hasZoom		=    8;
  hasTallTBar		=    2;
  toggleHilite		=    1;
For example, the Navigator palette uses palNoGrowProc for procID.
Note NewXWindow is also compatible with other non-standard window definition functions.
◊ PROCEDURE CloseXWindow(paramPtr XCmdPtr; window WindowPtr);
When an XCMD which manages an external window requests that the window be closed, it should call CloseXWindow. When all pending calls to the XCMD have returned, HyperCard sends an xCloseEvt to the XCMD. Only in response to this event should it dispose of its data structures and exit.
When the external window receives the xCloseEvt, it signals its willingness to close by setting paramPtr^.passFlag to TRUE. If this is not done, HyperTalk will not proceed with the deallocation of the window.
Any XCMD can close any external window by means of a call to CloseXWindow. CloseXWindow has no effect on windows that were not created by means of a call to NewXWindow or GetNewXWindow.
The receipt of an xCloseEvt is the signal that it is safe to deallocate memory and perform other cleanup operations.
Warning HyperTalk tries to close all open external windows when the user quits HyperCard. If any windows refuse to close at that time, HyperCard will not quit.
Another Warning Do not use any of the following Toolbox routines to close external windows CloseWindow, DisposeWindow, CloseDialog, or DisposDialog.

Window Utilities
◊ PROCEDURE HideHCPalettes(paramPtr XCmdPtr);
HideHCPalettes hides all of HyperCard’s built-in palettes (the tool window, the pattern window, the fatbits window, and the message box). It also sends an xHidePalettesEvt to all windows in the floating layer.
◊ PROCEDURE ShowHCPalettes(paramPtr XCmdPtr);
ShowHCPalettes reverses the effect of HideHCPalettes, showing all of HyperCard’s palettes that were visible when HideHCPalettes was called to hide them. It also sends an xShowPalettesEvt to all windows in the floating layer.
◊ PROCEDURE RegisterXWMenu(paramPtr XCmdPtr; window WindowPtr; menu MenuHandle;
                           registering BOOLEAN);
RegisterXWMenu is useful only to XCMDs that manage external windows. RegisterXWMenu informs HyperCard that the given menu is meant for use with the external window in window. When an item in the menu is selected by the user or specified as the menu item parameter to the doMenu command, the XCMD that registered the menu will receive a menu event (xMenuEvt). Note that an XCMD can register one of HyperCard’s menus, e.g. the Font menu, in order to temporarily borrow the menu.
RegisterXWMenu does not change the menubar. The XCMD must call the Menu Manager in order to insert or delete the menu and to redraw the menubar. Once the menu has been registered, it remains the property of the XCMD until RegisterXWMenu is called again with registering set to FALSE.
Note  HyperCard expects XCMD menus to have unique IDs but does not ensure that they do. You may employ the following function to find an unused menu ID.
FUNCTION UnusedMenuID INTEGER;
VAR thisID INTEGER;
    menuHndl MenuHandle;
BEGIN
  thisID = 1023;
  REPEAT
    thisID = thisID + 1;
    menuHndl = GetMHandle(thisID);
  UNTIL menuHndl = NIL;
  UnusedMenuID = thisID;
END;
◊ PROCEDURE SetXWIdleTime(paramPtr XCmdPtr; window WindowPtr; ticks LongInt);
XCMDs that manage external windows can request idle time from HyperCard. Use SetXWIdleTime if your XCMD needs to perform a periodic action.
Call SetXWIdleTime with a value greater than 0 for ticks to set the interval between periodic calls by HyperCard. HyperCard sends the XCMD a nullEvent when making its periodic call. To give up idle time, call SetXWIdleTime with a value of 0 for ticks.
Whether HyperCard’s periodic calls occur as often as requested depends on whether HyperCard is currently performing a time-critical or data-intensive operation. 
Note nullEvents are sent in both the foreground and background under MultiFinder, and under the Paint tools as well.

◊ PROCEDURE XWHasInterruptCode(paramPtr XCmdPtr; window WindowPtr; haveCode BOOLEAN);
When HyperCard creates an external window (by means of either the NewXWindow or GetNewXWindow callback), it is assumed that the code of the XCMD that manages it can be moved in memory while not executing.
If haveCode is TRUE, HyperCard will never unlock the relocatable block containing the XCMD code in memory. This allows ProcPtrs and other addresses within an XCMD’s code to be preserved and valid at all times. XWHasInterruptCode should be used with extreme prudence and should be undone as soon as possible— HyperCard’s memory management can become seriously taxed if there are any locked blocks inconveniently located in its heap. In particular, it’s impossible to open card windows at the full size of large cards when a nonrelocatable block is located too close to the bottom of the application heap.
The preferred method for an XCMD to manage procedure pointers passed to the Toolbox is to refresh them as needed rather than to call XWHasInterruptCode to ensure their validity. This method permits HyperCard to unlock the XCMD’s code between invocations. An example of such an XCMD is a text editor with a custom clikLoop routine. For best results, such an XCMD will recalculate and refresh its procPtr every time the user clicks in the viewRect. For example
  CASE evt.what OF
    mouseDown
      BEGIN
        GlobalToLocal(evt.where);
        hTE^^.clikLoop = @MyClikLoop;  { assembly language custom click loop }
        TEClick(evt.where, FALSE, hTE);
      END;

An XCMD that uses this method doesn’t need to call XWHasInterruptCode.
An XCMD’s code is always locked while it is actually executing. XWHasInterruptCode determines only whether the block of code can be unlocked when control is returned to HyperCard. 
◊ PROCEDURE XWAlwaysMoveHigh(paramPtr XCmdPtr; window WindowPtr; moveHigh BOOLEAN);
XWAlwaysMoveHigh tells HyperTalk to always move the external window’s code high on the heap before locking it down and calling it. External windows which may allocate large amounts of memory or send card messages to do such operations as go to another card should use this.
Background In normal XCMD and external window operations, HyperTalk makes a determination at the time the code is jumped to as to whether the code should be moved high in the application heap before being locked down. Currently, this is done whenever an XCMD is called, and when an external window is given the following events xOpenEvt, xMenuEvt, mouseDown, and keyDown. All other events are assumed to rarely cause memory allocation or messages to be sent, therefore HyperTalk can speed up the process of calling and returning from an external window. However, some external windows may elect to allocate memory on such things as an xSetPropEvt. For example, a Picture XCMD could be asked to change information about the currently-displayed PICT and that may cause memory to be allocated. A good rule of thumb is this if your external window is not called repeatedly in a script and you might allocate memory on events other than the aforementioned events, call this with moveHigh = TRUE.
◊ PROCEDURE XWAllowReEntrancy(paramPtr XCmdPtr; window WindowPtr; allowSysEvts BOOLEAN;
					allowHCEvts BOOLEAN);
Using XWAllowReEntrancy, an XCMD can tell HyperCard whether it is equipped to receive events (either system events or HyperCard-generated events) in a reentrant fashion. Either of the booleans, allowSysEvts or allowHCEvts, can be set to true or false to enable or disable (respectively) this behavior.
In certain situations, it is possible for an external window to generate events for itself. In other words, calling the Window Manager’s InvalRect routine on a portion of the window could create an updateEvt for the window. When is this updateEvt delivered?
While running scripts, HyperTalk periodically checks the Event Manager to see if update, activate, or MultiFinder events are pending. This is how HyperCard processes events in the background. In the course of these checks, the Window Manager may report an update event for the external window if it notices that any or all of the window is invalid. The problem arises from external windows that use SendCardMessage, SendHCMessage, EvalExpr, or any other HyperTalk callback that can run a script. XWAllowReentrancy was implemented to allow external windows to have asynchronous handling of certain events. For example, if a SendCardMessage callback causes a lengthy script to be executed, the external window may want to be notified that the user switched out of HyperCard under MultiFinder. To defend against unexpected events, an XCMD can use XWAllowReEntrancy to temporarily turn of reentrancy. 
Unlike system events, HyperTalk “events” are not queued, so unreceived events end up in the bit bucket.
Note HyperTalk defaults to not allowing reentrancy. 
Note Because xGetPropEvt and xSetPropEvt are events, if an external window has not called XWAllowReEntrancy(paramPtr,xxxx,TRUE), the scenario described below will result in the external window never receiving its xSetPropEvt for its visible property
	External Window “fred”	SendCardMessage(paramPtr,’go next card’);
	closeCard script		on closeCard
					  hide window “fred”  - this call will fail
					end closeCard
The xSetPropEvt will cause the external window to be reentered, so HyperTalk will skip the event and continue. If your external window needs to communicate with running scripts, make sure that you allow it to be reentrant.
Warning If an external window notifies HyperCard that it can receive reentrant system events, it may receive an unexpected updateEvt. Many Macintosh programmers use InvalRect or InvalRgn in order to allow asynchronous window updating. In applications, this allows more operations to take place between potentially time-consuming window redraws. However, any callback that executes lines of HyperTalk (eg SendCardMessage, EvalExpr) also polls the Event Manager for pending events. If there are no other pending events, the external window may receive a reentrant updateEvt at this time. To guard against that, use XWAllowReEntrancy to temporarily turn of reentrancy if this has a potential of happening.
Another Warning Writing completely reentrant code is difficult, and some development systems may not support reentrancy correctly. One tip is to make sure that every time an external window receives an event, it should completely save and restore its state. A good idea is to use the external window’s refCon field to store a handle to all the window’s state information. Beware of the use of “global variables” in stand alone code resources. See Macintosh Technical Note #256 for more information.

Text Editing Utilities
◊ PROCEDURE BeginXWEdit(paramPtr XCmdPtr; window WindowPtr);
BeginXWEdit registers an external window as the current editing environment. Once BeginXWEdit is called, HyperCard redirects all keystrokes to the XCMD, with the exception of command-key combinations recognized by the Menu Manger as the equivalent of menu items. In addition, HyperCard passes events to the XCMD that correspond to the first five items in the Edit menu whenever these items are selected. The XCMD still receives all of the other events pertaining to its window, including nullEvents if it has requested them.
Once the XCMD has registered itself as the current editing environment, it is liable to receive an xGiveUpEdit event from HyperCard when the user performs an action that activates a different editing environment, such as a click in the message box or in an unlocked field. When this happens, the XCMD should deactivate its editable area as appropriate, just as it does when its window is deactivated.
◊ PROCEDURE EndXWEdit(paramPtr XCmdPtr; window WindowPtr);
EndXWEdit informs HyperCard that an XCMD no longer wants to receive keystrokes and edit events for its external window. Call EndXWEdit before an external window that has edit is closed via a call to CloseXWindow.
◊ FUNCTION  HCWordBreakProc(paramPtr XCmdPtr) ProcPtr;
HCWordBreakProc returns a procedure pointer to HyperCard’s built-in word-break routine used for text in fields, scripts, and the message box. Script Editors and other text-editing XCMDs can use this address in the wordBreak field of a TextEdit record.
◊ PROCEDURE PrintTEHandle(paramPtr XCmdPtr; hTE TEHandle; header StringPtr);
Given a handle to a TextEdit record in hTE, PrintTEHandle displays a Print Job dialog and prints the record using the font, size, and style information contained within it. PrintTEHandle works for both old- and new-style edit records.

Script Editor Utilities
◊ PROCEDURE FormatScript(paramPtr XCmdPtr; scriptHndl Handle;
                         VAR insertionPoint LongInt; quickFormat BOOLEAN);
FormatScript reformats the script contained in the zero-terminated string to which scriptHndl is a handle. Call ZeroTermHandle before calling FormatScript if the text is not zero-terminated.
The offset into the text passed in insertionPoint is adjusted as necessary to reflect the same position within the text both before and after formatting. An XCMD that uses TextEdit should pass TEHandle^^.selStart for this parameter, then call TESetSelection after FormatScript returns. insertionPoint is also used when quickFormat is TRUE to determine which handler within the script should be formatted.
Note using EvalExpr to get the script of an object formats it automatically.
◊ FUNCTION  GetCheckPoints(paramPtr XCmdPtr) CheckPtHandle;
GetCheckPoints returns a handle to the cached checkpoints for the window’s script. The caller must dispose of the handle. If there are no cached checkpoints,  GetCheckPoints returns NIL.
The structure of a CheckPtHandle is
  CheckPtHandle = ^CheckPtPtr;
  CheckPtPtr    = ^CheckPts;
  CheckPts      = RECORD
                    checks ARRAY[1..16] OF INTEGER;
                  END;
◊ PROCEDURE SaveXWScript(paramPtr XCmdPtr; scriptText Handle);
Given a zero-terminated handle to a script in scriptText, SaveXWScript saves the script to disk. Call ZeroTermHandle before calling SaveXWScript if the text is not zero-terminated.
This callback is reserved for the use by XCMDs that implement script editors. It will fail if the XCMD has not been called by HyperCard as the current script editor. SaveXWScript will also fail if the text of the script exceeds 32K.
Note  An XCMD can set the script of any object using the following example code
SetGlobal(paramPtr,'tempGlobal',scriptHndl);
SendCardMessage(paramPtr,CONCAT('set the script of',objectName,'to tempGlobal'));
SendCardMessage(paramPtr,'put empty into tempGlobal');
◊ PROCEDURE SetCheckPoints(paramPtr XCmdPtr; checkLines CheckPtHandle);
SetCheckPoints sets the checkpoints of a script to the array of lines in checkLines.
◊ PROCEDURE GetObjectName(paramPtr XCmdPtr; object XTalkObjectPtr;
                          VAR objName Str255);
Given an XTalkObjectPtr as input, return the name of the object in objName.
◊ PROCEDURE GetObjectScript(paramPtr XCmdPtr; object XTalkObjectPtr;
                            VAR scriptHndl Handle);
Given an XTalkObjectPtr as input, return the script of the object in scriptHndl.
◊ PROCEDURE SetObjectScript(paramPtr XCmdPtr; object XTalkObjectPtr;
                            scriptHndl Handle);
Given an XTalkObjectPtr and a handle to a script as input, set the script of the object pointed by XTalkObjectPtr to the script in scriptHndl.
Debugger support
◊ PROCEDURE CountHandlers(paramPtr XCmdPtr; VAR handlerCount INTEGER);
CountHandlers returns the number of running handlers in handlerCount. If handlerCount is 0, then no handlers are currently running and the only variables that exist are global variables. 
◊ PROCEDURE GetHandlerInfo(paramPtr XCmdPtr; handlerNum INTEGER;
                           VAR handlerName Str255; VAR objectName Str255;
                           VAR varCount INTEGER);
GetHandlerInfo returns information about handlers. If handlerNum is 1, GetHandlerInfo returns information about the current handler, if there is one. If handlerNum is greater than 1, GetHandlerInfo returns information about a handler that has a pending call to another handler; for example, if handlerNum is 2, the information returned is for the handler that called the current handler. If there is no running handler, passing 0 for handlerNum yields information about the global variables.
Note The built-in Variable Watcher only looks at the current handler, or the global scope (handler #0) if no handler is running.
◊ PROCEDURE GetVarInfo(paramPtr XCmdPtr; handlerNum INTEGER; varNum INTEGER;
                       VAR varName Str255; VAR isGlobal BOOLEAN;
                       VAR varValue Str255; varHndl Handle);
Given the variable number varNum in active handler number handlerNum, GetVarInfo returns information about the variable. If varHndl is NIL, a truncated version of the contents of the variable, limited to 255 characters, is returned in varValue. If varHndl is a valid handle, the entire contents of the variable are copied into that handle, which is resized as necessary. If there is no running handler, passing 0 for handlerNum returns information about the global variables.
◊ PROCEDURE SetVarValue(paramPtr XCmdPtr; handlerNum INTEGER; varNum INTEGER;
                        varHndl Handle);
SetVarValue sets the value of variable number varNum of handler handlerNum to the zero-terminated string passed in varHndl.
Note Changing the value of a variable other than the global variables (handlerNum 0) or  the variables local to the current handler (handlerNum 1) will not effect the handler until it is once again current.
◊ AbortScript(paramPtr XCmdPtr);
AbortScript cancels the currently executing handlers. The effect is the same as pressing Command-Period. 
Note  An XCMD that calls AbortScript is not itself aborted. It will exit normally.
◊ PROCEDURE GoScript(paramPtr XCmdPtr);
GoScript exits the debugger, closes all temporary debugger windows, and continues normal execution of scripts.
◊ PROCEDURE StepScript(paramPtr XCmdPtr; stepInto BOOLEAN);
StepScript creates a temporary checkpoint after the currently executing line. If stepInto is true, the checkpoint is created at the next line of HyperTalk to be executed, even if it belongs to another handler. If stepInto is false, the checkpoint is created at the next line of the current handler. StepScript will cause the Debugger to step out of a handler if the current line is its last.

◊ PROCEDURE TraceScript(paramPtr XCmdPtr; traceInto BOOLEAN);
TraceScript is similar to StepScript above, except that execution continues after the line is hilited by the debugger. TraceScript  respects the new HyperTalk property the traceDelay.
◊ FUNCTION  GetStackCrawl(paramPtr XCmdPtr) Handle;
Returns a zero-terminated handle to the chain of callers (indented) as in the Message Watcher.

Callbacks That Move Memory
When writing an XCMD, be aware that many HyperTalk callbacks can cause memory in the heap to move. Macintosh Technical Note #117 Compatibility Why & How contains a complete discussion of the pitfalls of Toolbox calls and procedure calls which move memory. Read it if you are not already familiar with it. The best test for susceptibility to these problem is to use your debugger’s heap scramble when you make a callback.

BeginXSound
BeginXWEdit
CloseXWindow
CountHandlers
EndXSound
EndXWEdit
EvalExpr
FormatScript
GetCheckPoints
GetFieldByID
GetFieldByName
GetFieldByNum
GetFieldTE
GetFilePath
GetGlobal
GetHandlerInfo
GetNewXWindow
GetObjectName
GetObjectScript
GetStackCrawl
GetVarInfo
HideHCPalettes
NewXWindow
Notify
PasToZero
PointToStr
PrintTEHandle
RectToStr
RunHandler
SaveXWScript
SendCardMessage
SendHCEvent
SendHCMessage
SendWindowMessage
SetCheckPoints
SetFieldByID
SetFieldByName
SetFieldByNum
SetFieldTE
SetGlobal
SetObjectScript
SetVarValue
ShowHCPalettes
StackNameToNum
StrToPoint
StrToRect
StringEqual
StringLength
StringMatch
ZeroTermHandle

New XCMD Interface files
This version of the MPW file HyperXCMD.p defines the HyperCard XCMD interface

(*
    HyperXCmd.p
    Definition file for HyperCard XCMDs and XFCNs in Pascal.
    Copyright Apple Computer, Inc. 1987-1990
    All Rights Reserved
*)

UNIT HyperXCmd;
INTERFACE

CONST
  { result codes }
  xresSucc = 0;
  xresFail = 1;
  xresNotImp = 2;

  { XCMDBlock constants for event.what... }
  xOpenEvt              = 1000;         { the first event after you are created }
  xCloseEvt             = 1001;         { your window is being forced close (Quit?) }
  xGiveUpEditEvt        = 1002;         { you are losing Edit... }
  xGiveUpSoundEvt       = 1003;         { someone else is requesting HyperCard's sound channel }
  xHidePalettesEvt      = 1004;         { someone called HideHCPalettes }
  xShowPalettesEvt      = 1005;         { someone called ShowHCPalettes }
  xEditUndo             = 1100;         { Edit——Undo }
  xEditCut              = 1102;         { Edit——Cut }
  xEditCopy             = 1103;         { Edit——Copy }
  xEditPaste            = 1104;         { Edit——Paste }
  xEditClear            = 1105;         { Edit——Clear }
  xSendEvt              = 1200;         { script has sent you a message (text) }
  xSetPropEvt           = 1201;         { set a window property }
  xGetPropEvt           = 1202;         { get a window property }
  xCursorWithin         = 1300;         { cursor is within the window }
  xMenuEvt              = 1400;         { user has selected an item in your menu }
  xMBarClickedEvt       = 1401;         { a menu is about to be shown--update if needed }

  xShowWatchInfoEvt     = 1501;         { for variable and message watchers }
  xScriptErrorEvt       = 1502;         { place the insertion point }
  xDebugErrorEvt        = 1503;         { user clicked “Debug” at a complaint }
  xDebugStepEvt         = 1504;         { hilite the line }
  xDebugTraceEvt        = 1505;         { same as step but tracing }
  xDebugFinishedEvt     = 1506;         { script ended }

  paletteProc           = 2048;         { Windoid with grow box }
  palNoGrowProc         = 2052;         { standard Windoid defproc } 
  palZoomProc           = 2056;         { Windoid with zoom and grow }
  palZoomNoGrow         = 2060;         { Windoid with zoom and no grow }
  hasZoom               =    8;
  hasTallTBar           =    2;
  toggleHilite          =    1;
  
  maxCachedChecks       = 16;           { maximum number of checkpoints in a script }

  { paramCount is set to these constants when first calling special XThings }
  xMessageWatcherID     = -2;
  xVariableWatcherID    = -3;
  xScriptEditorID       = -4;
  xDebuggerID           = -5;

  { XTalkObjectPtr^.objectKind values }
  stackObj      = 1;
  bkgndObj      = 2;
  cardObj       = 3;
  fieldObj      = 4;
  buttonObj     = 5;

  { selectors for ShowHCAlert's dialogs (shown as buttonIDbuttonText) }
  errorDlgID            = 1;    { 1OK (default) }
  confirmDlgID          = 2;    { 1OK (default) and 2Cancel }
  confirmDelDlgID       = 3;    { 1Cancel (default) and 2Delete }
  yesNoCancelDlgID      = 4;    { 1Yes (default), 2Cancel, and 3No }

TYPE
  XCmdPtr   = ^XCmdBlock;
  XCmdBlock = RECORD
                paramCount     INTEGER; { If = -1 then new use for XWindoids }
                params         ARRAY[1..16] OF Handle;
                returnValue    Handle; 
                passFlag       BOOLEAN;
                
                entryPoint     ProcPtr; { to call back to HyperCard }
                request        INTEGER;
                result         INTEGER;
                inArgs         ARRAY[1..8] OF LongInt;
                outArgs        ARRAY[1..4] OF LongInt;
              END;

  XWEventInfoPtr = ^XWEventInfo;
  XWEventInfo    = RECORD
                     event       EventRecord;
                     eventWindow WindowPtr;
                     eventParams ARRAY[1..9] OF LongInt;
                     eventResult Handle;
                   END;
                  
  XTalkObjectPtr = ^XTalkObject;
  XTalkObject = RECORD
                  objectKind   INTEGER;        { stack, bkgnd, card, field, or button }
                  stackNum     LongInt;        { reference number of the source stack }
                  bkgndID      LongInt;
                  cardID       LongInt;
                  buttonID     LongInt;
                  fieldID      LongInt;
                END;

  CheckPtHandle = ^CheckPtPtr;
  CheckPtPtr    = ^CheckPts;
  CheckPts      = RECORD
                    checks ARRAY[1..maxCachedChecks] OF INTEGER;
                  END;

(****  HyperTalk Utilities  ****)
FUNCTION  EvalExpr(paramPtr XCmdPtr; expr Str255) Handle;
PROCEDURE SendCardMessage(paramPtr XCmdPtr; msg Str255);
PROCEDURE SendHCMessage(paramPtr XCmdPtr; msg Str255);
PROCEDURE RunHandler(paramPtr XCmdPtr; handler Handle);

(****  Memory Utilities  ****)
FUNCTION  GetGlobal(paramPtr XCmdPtr; globName Str255) Handle;
PROCEDURE SetGlobal(paramPtr XCmdPtr; globName Str255; globValue Handle);
PROCEDURE ZeroBytes(paramPtr XCmdPtr; dstPtr Ptr;longCount LongInt);

(****  String Utilities  ****)
PROCEDURE ScanToReturn(paramPtr XCmdPtr; VAR scanPtr Ptr);
PROCEDURE ScanToZero(paramPtr XCmdPtr; VAR scanPtr Ptr);
FUNCTION  StringEqual(paramPtr XCmdPtr; str1,str2 Str255) BOOLEAN;
FUNCTION  StringLength(paramPtr XCmdPtr; strPtr Ptr) LongInt;
FUNCTION  StringMatch(paramPtr XCmdPtr; pattern Str255; target Ptr) Ptr;
PROCEDURE ZeroTermHandle(paramPtr XCmdPtr; hndl Handle);      

(****  String Conversions  ****)
PROCEDURE BoolToStr(paramPtr XCmdPtr; bool BOOLEAN; VAR str Str255);
PROCEDURE ExtToStr(paramPtr XCmdPtr; num Extended; VAR str Str255);
PROCEDURE LongToStr(paramPtr XCmdPtr; posNum LongInt; VAR str Str255);
PROCEDURE NumToHex(paramPtr XCmdPtr; num LongInt; nDigits INTEGER; VAR str Str255);
PROCEDURE NumToStr(paramPtr XCmdPtr; num LongInt; VAR str Str255);
FUNCTION  PasToZero(paramPtr XCmdPtr; str Str255) Handle;
PROCEDURE PointToStr(paramPtr XCmdPtr; pt Point; VAR str Str255);
PROCEDURE RectToStr(paramPtr XCmdPtr; rct Rect; VAR str Str255);
PROCEDURE ReturnToPas(paramPtr XCmdPtr; zeroStr Ptr; VAR pasStr Str255);
FUNCTION  StrToBool(paramPtr XCmdPtr; str Str255) BOOLEAN;
FUNCTION  StrToExt(paramPtr XCmdPtr; str Str255) Extended;
FUNCTION  StrToLong(paramPtr XCmdPtr; str Str255) LongInt;
FUNCTION  StrToNum(paramPtr XCmdPtr; str Str255) LongInt;
PROCEDURE StrToPoint(paramPtr XCmdPtr; str Str255; VAR pt Point);
PROCEDURE StrToRect(paramPtr XCmdPtr; str Str255; VAR rct Rect);
PROCEDURE ZeroToPas(paramPtr XCmdPtr; zeroStr Ptr; VAR pasStr Str255);

(****  Field Utilities  ****)
FUNCTION  GetFieldByID(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldID INTEGER) Handle;
FUNCTION  GetFieldByName(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldName Str255) Handle;
FUNCTION  GetFieldByNum(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldNum INTEGER) Handle;
PROCEDURE SetFieldByID(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldID INTEGER; fieldVal Handle);
PROCEDURE SetFieldByName(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldName Str255; fieldVal Handle);
PROCEDURE SetFieldByNum(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldNum INTEGER; fieldVal Handle);
FUNCTION  GetFieldTE(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldID,fieldNum INTEGER;
                     fieldNamePtr StringPtr) TEHandle;
PROCEDURE SetFieldTE(paramPtr XCmdPtr; cardFieldFlag BOOLEAN; fieldID,fieldNum INTEGER;
                     fieldNamePtr StringPtr; fieldTE TEHandle);

(****  Miscellaneous Utilities  ****)
PROCEDURE BeginXSound(paramPtr XCmdPtr; window WindowPtr);                    
PROCEDURE EndXSound(paramPtr XCmdPtr);                         
FUNCTION  GetFilePath(paramPtr XCmdPtr; fileName Str255; numTypes INTEGER; typeList SFTypeList;
                      askUser BOOLEAN; VAR fileType OSType; VAR fullName Str255) BOOLEAN;
PROCEDURE GetXResInfo(paramPtr XCmdPtr; VAR resFile INTEGER; VAR resID INTEGER;
                      VAR rType ResType; VAR name Str255);
PROCEDURE Notify(paramPtr XCmdPtr);
PROCEDURE SendHCEvent(paramPtr XCmdPtr; event EventRecord);   
PROCEDURE SendWindowMessage(paramPtr XCmdPtr; windPtr WindowPtr;
                            windowName Str255; msg Str255);
FUNCTION  FrontDocWindow(paramPtr XCmdPtr) WindowPtr;
FUNCTION  StackNameToNum(paramPtr XCmdPtr; stackName Str255) LongInt;
FUNCTION  ShowHCAlert(paramPtr XCMDPtr; dlgID INTEGER; promptStr Str255) INTEGER;

(****  Creating and Disposing XWindoids  ****)
FUNCTION  NewXWindow(paramPtr XCmdPtr; boundsRect Rect; title Str255; visible BOOLEAN;
                     procID INTEGER; color BOOLEAN; floating BOOLEAN) WindowPtr;            
FUNCTION  GetNewXWindow(paramPtr XCmdPtr; templateType ResType; templateID INTEGER;
                        color BOOLEAN; floating BOOLEAN) WindowPtr;          
PROCEDURE CloseXWindow(paramPtr XCmdPtr; window WindowPtr);   

(****  XWindoid Utilities  ****)
PROCEDURE HideHCPalettes(paramPtr XCmdPtr);
PROCEDURE ShowHCPalettes(paramPtr XCmdPtr);
PROCEDURE RegisterXWMenu(paramPtr XCmdPtr; window WindowPtr; menu MenuHandle; registering BOOLEAN);
PROCEDURE SetXWIdleTime(paramPtr XCmdPtr; window WindowPtr; interval LongInt);
PROCEDURE XWHasInterruptCode(paramPtr XCmdPtr; window WindowPtr; haveCode BOOLEAN);
PROCEDURE XWAlwaysMoveHigh(paramPtr XCmdPtr; window WindowPtr; moveHigh BOOLEAN);
PROCEDURE XWAllowReEntrancy(paramPtr XCmdPtr; window WindowPtr; allowSysEvts BOOLEAN; allowHCEvts BOOLEAN);

(****  Text Editing Utilities  ****)
PROCEDURE BeginXWEdit(paramPtr XCmdPtr; window WindowPtr);                    
PROCEDURE EndXWEdit(paramPtr XCmdPtr; window WindowPtr);                              
FUNCTION  HCWordBreakProc(paramPtr XCmdPtr) ProcPtr;          
PROCEDURE PrintTEHandle(paramPtr XCmdPtr; hTE TEHandle; header StringPtr);

(****  Script Editor support  ****)
FUNCTION  GetCheckPoints(paramPtr XCmdPtr) CheckPtHandle;     
PROCEDURE SetCheckPoints(paramPtr XCmdPtr; checkLines CheckPtHandle);
PROCEDURE FormatScript(paramPtr XCmdPtr; scriptHndl Handle; 
                       VAR insertionPoint LongInt; quickFormat BOOLEAN);
PROCEDURE SaveXWScript(paramPtr XCmdPtr; scriptHndl Handle);  
PROCEDURE GetObjectName(paramPtr XCmdPtr; object XTalkObjectPtr; VAR objName Str255);
PROCEDURE GetObjectScript(paramPtr XCmdPtr; object XTalkObjectPtr; VAR scriptHndl Handle);
PROCEDURE SetObjectScript(paramPtr XCmdPtr; object XTalkObjectPtr; scriptHndl Handle);

(****  Debugging Tools support  ****)
PROCEDURE AbortScript(paramPtr XCmdPtr);
PROCEDURE GoScript(paramPtr XCmdPtr);
PROCEDURE StepScript(paramPtr XCmdPtr; stepInto BOOLEAN);
PROCEDURE CountHandlers(paramPtr XCmdPtr; VAR handlerCount INTEGER);
PROCEDURE GetHandlerInfo(paramPtr XCmdPtr; handlerNum INTEGER; VAR handlerName Str255;
                         VAR objectName Str255; VAR varCount INTEGER);
PROCEDURE GetVarInfo(paramPtr XCmdPtr; handlerNum INTEGER; varNum INTEGER;
                     VAR varName Str255; VAR isGlobal BOOLEAN; VAR varValue Str255;
                     varHndl Handle);
PROCEDURE SetVarValue(paramPtr XCmdPtr; handlerNum INTEGER; varNum INTEGER;
                      varHndl Handle);
FUNCTION  GetStackCrawl(paramPtr XCmdPtr) Handle;
PROCEDURE TraceScript(paramPtr XCmdPtr; traceInto BOOLEAN);

END.